<?php

namespace pUtils;

use PDO;
use PDOException;

class take {
    
    public string|null $errMsg       = null;
    private PDO|null   $conn;
    private array      $prepareStmts = [];
    private int        $apiCodeOk;
    private int        $apiCodeErr;
    
    /**
     * @param null     $db    PT105
     * @param null     $debug 使用debug模式将打印sql执行产生的错误信息，默认false
     * @param int|null $json_options
     */
    function __construct(private $db = null, private $debug = null, private ?int $json_options = 0) {
        if (!isset($db)) {
            $this->db = config::get("global", "defaultDB");
        }
        if (!isset($debug)) {
            $this->debug = +config::get("global", "debug");
        }
        $this->apiCodeOk  = +(config::get("global", "api", "code", "ok") ?? 200);
        $this->apiCodeErr = +(config::get("global", "api", "code", "err") ?? 0);
    }
    
    function __destruct() {
        $this->close();
    }
    
    /** 关闭所有预处理缓存，以及建立的连接 */
    function close() {
        foreach ($this->prepareStmts as $i => $p) {
            $p?->closeCursor();
            unset($this->prepareStmts[$i]);
        }
        if ($this->conn ?? null) {
            $this->conn = null;
        }
    }
    
    /**开启事务 */
    function begin() {
        ($this->conn = $this->conn ?? (new db)?->conn($this->db))?->beginTransaction();
    }
    
    /**回滚当前开启的事务 */
    function rollback() {
        $this->conn?->rollback();
    }
    
    /**提交当前开启的事务 */
    function commit() {
        $this->conn?->commit();
    }
    
    /** 获取sql结果集的第一行，sql执行失败将返回null */
    function getOne(string $sql = "", ...$params) {
        return $this->exec($sql, ...$params)?->fetch();
    }
    
    function exec(string $sql = "", ...$params) {
        try {
            $this->errMsg = null;
            
            return ($stmt = $this->prepare($sql))?->execute($params) ? $stmt : null;
        } catch (PDOException $e) {
            $this->errMsg = $e->getMessage();
            
            throw $e;
        }
    }
    
    private function prepare($sql) {
        if (!isset($this->prepareStmts[$sql]) || !$this->prepareStmts[$sql]) {
            $this->prepareStmts[$sql] = ($this->conn ?? ($this->conn = (new db)?->conn($this->db)))?->prepare($sql);
        }
        
        return $this->prepareStmts[$sql];
    }
    
    /** 获取sql结果集的第一行，并返回响应结构的json */
    function getOneJSON(string $sql = "", ...$params): bool|string {
        return $this->json($this->exec($sql, ...$params)?->fetch(), action: "获取");
    }
    
    private function json($data = null, $code = null, string $msg = null, string $action = null): bool|string {
        return json_encode([
                               // "code" => $code = $code ? : ($this->errMsg ? $this->apiCodeErr : $this->apiCodeOk),
                               // "msg" => $msg ?? $action . ($code == 0 ? "成功" : "失败") . ($this->debug && $this->errMsg ? "：" . $this->errMsg : null),
                               "code" => $code = $code ? : ($this->errMsg ? $this->apiCodeErr : $this->apiCodeOk),
                               "msg"  => $msg ?? $action . ($code == $this->apiCodeOk ? "成功" : "失败") . ($this->debug && $this->errMsg ? "：" . $this->errMsg : null),
                               "data" => $data === false ? null : $data,
                           ], $this->json_options);
    }
    
    /** 获取sql结果集的所有行，sql执行失败将返回null */
    function get(string $sql = "", ...$params): array|null {
        return $this->exec($sql, ...$params)?->fetchAll();
    }
    
    /** 获取sql结果集的所有行，并返回响应结构的json */
    function getJSON(string $sql = "", ...$params): bool|string {
        return $this->json(($this->exec($sql, ...$params))?->fetchAll(), action: "获取");
    }
    
    /** 执行insert类sql，并返回响应结构的json */
    function addJSON(string $sql = "", ...$params): bool|string {
        return $this->json(code: $this->exec($sql, ...$params), action: "添加");
    }
    
    function updJSON(string $sql = "", ...$params): bool|string {
        return $this->json(code: $this->exec($sql, ...$params), action: "更新");
    }
    
    function delJSON(string $sql = "", ...$params): bool|string {
        return $this->json(code: $this->exec($sql, ...$params), action: "删除");
    }
    
    function fileJSON(string $sql = "", ...$params): bool|string {
        return $this->json(code: $this->exec($sql, ...$params), action: "上传");
    }
    
    function lastInsertId(string $name): string {
        return $this->conn?->lastInsertId($name);
    }
}